package cc.mrbird.febs.system.service.impl;

import cc.mrbird.febs.accounting.dto.company.CompanyDTO;
import cc.mrbird.febs.accounting.dto.project.ProjectInfoDTO;
import cc.mrbird.febs.accounting.entity.Company;
import cc.mrbird.febs.accounting.entity.CompanyProject;
import cc.mrbird.febs.accounting.entity.Project;
import cc.mrbird.febs.accounting.mapper.CompanyMapper;
import cc.mrbird.febs.accounting.mapper.CompanyProjectMapper;
import cc.mrbird.febs.accounting.mapper.ProjectMapper;
import cc.mrbird.febs.common.entity.FebsConstant;
import cc.mrbird.febs.common.entity.QueryRequest;
import cc.mrbird.febs.common.entity.Strings;
import cc.mrbird.febs.common.event.UserAuthenticationUpdatedEventPublisher;
import cc.mrbird.febs.common.utils.FebsUtil;
import cc.mrbird.febs.common.utils.SortUtil;
import cc.mrbird.febs.system.entity.Role;
import cc.mrbird.febs.system.entity.RoleMenu;
import cc.mrbird.febs.system.mapper.RoleMapper;
import cc.mrbird.febs.system.service.IRoleMenuService;
import cc.mrbird.febs.system.service.IRoleService;
import cc.mrbird.febs.system.service.IUserRoleService;
import cc.mrbird.febs.utils.CommonConstant;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Predicate;

/**
 * @author MrBird
 */
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IRoleService {

    private final IRoleMenuService roleMenuService;
    private final IUserRoleService userRoleService;
    private final UserAuthenticationUpdatedEventPublisher publisher;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private CompanyProjectMapper companyProjectMapper;

    @Resource
    private CompanyMapper companyMapper;

    @Resource
    private cc.mrbird.febs.accounting.mapper.ProjectMapper projectMapper;

    @Override
    public List<Role> findUserRole(String username) {
        return baseMapper.findUserRole(username);
    }

    @Override
    public List<Role> findRoles(Role role) {
        String id = FebsUtil.getCurrentUser().getRoleId();
        QueryWrapper<Role> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(role.getRoleName())) {
            queryWrapper.lambda().like(Role::getRoleName, role.getRoleName());
        }
        if (!id.equals("1"))
            queryWrapper.lambda().ne(Role::getRoleId, 1);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public IPage<Role> findRoles(Role role, QueryRequest request) {
        Integer flag = FebsUtil.getCurrentUser().getUserId() == 1L ? 1:0;
        Page<Role> page = new Page<>(request.getPageNum(), request.getPageSize());
        page.setSearchCount(false);
        page.setTotal(baseMapper.countRole(role, flag));
        SortUtil.handlePageSort(request, page, "createTime", FebsConstant.ORDER_DESC, false);
        return baseMapper.findRolePage(page, role, flag);
    }

    @Override
    public Role findByName(String roleName) {
        return baseMapper.selectOne(new QueryWrapper<Role>().lambda().eq(Role::getRoleName, roleName));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createRole(Role role) {
        role.setCreateTime(new Date());
        baseMapper.insert(role);
        saveRoleMenus(role);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRole(Role role) {
        role.setModifyTime(new Date());
        updateById(role);
        List<String> roleIdList = Lists.newArrayList(String.valueOf(role.getRoleId()));
        roleMenuService.deleteRoleMenusByRoleId(roleIdList);
        saveRoleMenus(role);
        Set<Long> userIds = userRoleService.findUserIdByRoleId(role.getRoleId());
        if (CollectionUtils.isNotEmpty(userIds)) {
            publisher.publishEvent(userIds);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRoles(String roleIds) {
        List<String> list = Arrays.asList(roleIds.split(Strings.COMMA));
        baseMapper.delete(new QueryWrapper<Role>().lambda().in(Role::getRoleId, list));

        roleMenuService.deleteRoleMenusByRoleId(list);
        userRoleService.deleteUserRolesByRoleId(list);

        Set<Long> userIds = userRoleService.findUserIdByRoleIds(list);
        if (CollectionUtils.isNotEmpty(userIds)) {
            publisher.publishEvent(userIds);
        }
    }

    private void saveRoleMenus(Role role) {
        if (StringUtils.isNotBlank(role.getMenuIds())) {
            String[] menuIds = role.getMenuIds().split(Strings.COMMA);
            List<RoleMenu> roleMenus = Lists.newArrayList();
            Arrays.stream(menuIds).forEach(menuId -> {
                RoleMenu roleMenu = new RoleMenu();
                roleMenu.setMenuId(Long.valueOf(menuId));
                roleMenu.setRoleId(role.getRoleId());
                roleMenus.add(roleMenu);
            });
            roleMenuService.saveBatch(roleMenus);
        }
    }


    public List<CompanyDTO> getCompanyProjectByUserId() {
        Role role = roleMapper.selectById(FebsUtil.getCurrentUser().getRoleId());
        boolean byCompany = role.getPermissionType() == 0;

        // 获取权限列表
        String per = byCompany ? role.getCompanyPermission() : role.getProjectPermission();
        List<String> perList = per == null ? new ArrayList<>() : Arrays.asList(per.split(","));

        LambdaQueryWrapper<CompanyProject> q1 = new LambdaQueryWrapper<>();
        if (!(Objects.equals(role.getRoleId(), CommonConstant.HZTT_ROLE_ID))) {
            if (byCompany) q1.in(CompanyProject::getCompanyId, perList);
            else q1.in(CompanyProject::getProjectId, perList);
        }
        List<CompanyProject> companyProjects = companyProjectMapper.selectList(q1);

        // 获取公司和项目信息，懒得建mapper
        List<Long> cid = new ArrayList<>();
        List<Long> pid = new ArrayList<>();

        for (CompanyProject companyProject : companyProjects) {
            cid.add(companyProject.getCompanyId());
            pid.add(companyProject.getProjectId());
        }

        LambdaQueryWrapper<Company> q2 = new LambdaQueryWrapper<>();
        q2.eq(Company::getIsDeleted, 0);
        q2.in(Company::getId, cid);
        List<Company> companies = companyMapper.selectList(q2);

        LambdaQueryWrapper<Project> q3 = new LambdaQueryWrapper<>();
        q3.eq(Project::getIsDeleted, 0);
        q3.in(Project::getId, pid);
        List<Project> projects = projectMapper.selectList(q3);

        // 封装DTO
        List<CompanyDTO> companyDTOS = new ArrayList<>();
        for (CompanyProject companyProject : companyProjects) {
            boolean hasCompany = false;
            for (CompanyDTO companyDTO : companyDTOS) {
                if (Objects.equals(companyDTO.getCompanyId(), companyProject.getCompanyId())) {
                    hasCompany = true;
                    break;
                }
            }
            // 如果company不存在存在，添加
            if (!hasCompany) {
                // 获取公司名字
                for (Company company : companies) {
                    if (Objects.equals(company.getId(), companyProject.getCompanyId())) {
                        CompanyDTO companyDTO = new CompanyDTO()
                                .setCompanyId(company.getId())
                                .setCompanyName(company.getCompanyName());
                        companyDTOS.add(companyDTO);
                    }
                }
            }
            for (CompanyDTO companyDTO : companyDTOS) {
                // 如果company已经存在，向projectList添加信息
                if (Objects.equals(companyDTO.getCompanyId(), companyProject.getCompanyId())) {
                    // 获取项目名称
                    for (Project project : projects) {
                        if (Objects.equals(project.getId(), companyProject.getProjectId())) {
                            ProjectInfoDTO projectInfoDTO = new ProjectInfoDTO()
                                    .setProjectId(project.getId())
                                    .setProjectName(project.getProjectName());
                            companyDTO.getProjectInfos().add(projectInfoDTO);
                            break;
                        }
                    }
                    break;
                }
            }
        }
        // 定义一个Predicate来指定删除条件，这里的条件是移除不包含项目的公司
//        Predicate<CompanyDTO> hasNoPro = companyDTO -> companyDTO.getProjectInfos().isEmpty();
//        companyDTOS.removeIf(hasNoPro);
        return companyDTOS;
    }
}
